
#include "Shape.hpp"
#include <vector>


void SphereRaw::init(const float radius, const unsigned int resolution)
{
  const unsigned int numVertices = (resolution+2u)*resolution*2u;
  std::vector<float> vertices(3*numVertices);
   
   
  float theta2, phi;    // next theta angle, phi angle
  float ct, st;         // cos(theta), sin(theta)
  float ct2, st2;       // cos(next theta), sin(next theta)
  float cp, sp;         // cos(phi), sin(phi)
  
  const float Pi    = M_PI;
  const float TwoPi = 2.0f * Pi;
  const float Delta = 1.0f / resolution;
  
  ct2 = 0.0f; st2 = -1.0f;
  
  /* Create a sphere from bottom to top (like a spiral) as a tristrip */
  int id = 0;
	for (int j=0; j<resolution; ++j)
	{    
    ct = ct2;
    st = st2;
    
    theta2 = ((j+1) * Delta - 0.5f) * Pi;
    ct2 = glm::cos( theta2 );
    st2 = glm::sin( theta2 );
    
    vertices[id + 0] = radius * (ct);
    vertices[id + 1] = radius * (st);
    vertices[id + 2] = 0.0f;
    id+=3;
    
		for (int i=0; i<resolution+1; ++i)
		{
      phi = TwoPi * i * Delta;
      cp = glm::cos(phi);
      sp = glm::sin(phi);      
			
      vertices[id + 0] = radius * (ct2 * cp);
      vertices[id + 1] = radius * (st2);
      vertices[id + 2] = radius * (ct2 * sp);
      id+=3;
      
      vertices[id + 0] = radius * (ct * cp);
      vertices[id + 1] = radius * (st);
      vertices[id + 2] = radius * (ct * sp);
      id+=3;
		}
    vertices[id + 0] = radius * (ct2);
    vertices[id + 1] = radius * (st2);
    vertices[id + 2] = 0.0f;
    id+=3;
	}
  
  
  setAttrib( POSITION, vertices.data(), 3, numVertices);
  setPrimitiveType( GL_TRIANGLE_STRIP );
  
  upload( aer::DeviceBuffer::STATIC_DRAW, SINGLE_BUFFER);
}


void Dome::init(const float radius, const unsigned int resolution)
{
  AER_ASSERT( "(warning) not implemented yet." && 0 );
}


void Disc::init(const float radius, const unsigned int resolution)
{
  AER_ASSERT( "(warning) not implemented yet." && 0 );
}


void CubeRaw::init(const float side)
{
  const float c = 0.5f*side;
  
  const float vertices[] =
  {
    +c, +c, +c,   +c, -c, +c,   +c, -c, -c,   +c, +c, -c, // +X
    -c, +c, +c,   -c, +c, -c,   -c, -c, -c,   -c, -c, +c, // -X
    +c, +c, +c,   +c, +c, -c,   -c, +c, -c,   -c, +c, +c, // +Y
    +c, -c, +c,   -c, -c, +c,   -c, -c, -c,   +c, -c, -c, // -Y
    +c, +c, +c,   -c, +c, +c,   -c, -c, +c,   +c, -c, +c, // +Z
    +c, +c, -c,   +c, -c, -c,   -c, -c, -c,   -c, +c, -c  // -Z
  };
  
  const unsigned int indices[] =
  {
    0, 1, 2, 0, 2, 3,
		4, 5, 6, 4, 6, 7,
		8, 9, 10, 8, 10, 11,
		12, 13, 14, 12, 14, 15,
		16, 17, 18, 16, 18, 19,
    20, 21, 22, 20, 22, 23
  };
  
  const unsigned int numIndices = sizeof(indices) / sizeof(unsigned int);
  
  setAttrib( POSITION, vertices, 3, sizeof(vertices)/3);
  setIndices( GL_TRIANGLES, indices, numIndices);
  upload( aer::DeviceBuffer::STATIC_DRAW, SINGLE_BUFFER);
}

void Cube::init(const float side)
{
  // [WARNING] Redundant with CubeRaw..
  
  const float c = 0.5f*side;
  
  const float vertices[] =
  {
    +c, +c, +c,   +c, -c, +c,   +c, -c, -c,   +c, +c, -c, // +X
    -c, +c, +c,   -c, +c, -c,   -c, -c, -c,   -c, -c, +c, // -X
    +c, +c, +c,   +c, +c, -c,   -c, +c, -c,   -c, +c, +c, // +Y
    +c, -c, +c,   -c, -c, +c,   -c, -c, -c,   +c, -c, -c, // -Y
    +c, +c, +c,   -c, +c, +c,   -c, -c, +c,   +c, -c, +c, // +Z
    +c, +c, -c,   +c, -c, -c,   -c, -c, -c,   -c, +c, -c  // -Z
  };
  
  const float texCoords[] =
  {
    1.0f, 1.0f,  0.0f, 1.0f,  0.0f, 0.0f,  1.0f, 0.0f,
    1.0f, 1.0f,  0.0f, 1.0f,  0.0f, 0.0f,  1.0f, 0.0f,
    1.0f, 1.0f,  0.0f, 1.0f,  0.0f, 0.0f,  1.0f, 0.0f,
    1.0f, 1.0f,  0.0f, 1.0f,  0.0f, 0.0f,  1.0f, 0.0f,
    1.0f, 1.0f,  0.0f, 1.0f,  0.0f, 0.0f,  1.0f, 0.0f,
    1.0f, 1.0f,  0.0f, 1.0f,  0.0f, 0.0f,  1.0f, 0.0f    
  };
  
  const float normals[] =
  {
    +1.0f, 0.0f, 0.0f,  +1.0f, 0.0f, 0.0f,  +1.0f, 0.0f, 0.0f,  +1.0f, 0.0f, 0.0f,
    -1.0f, 0.0f, 0.0f,  -1.0f, 0.0f, 0.0f,  -1.0f, 0.0f, 0.0f,  -1.0f, 0.0f, 0.0f,
    
    0.0f, +1.0f, 0.0f,  0.0f, +1.0f, 0.0f,  0.0f, +1.0f, 0.0f,  0.0f, +1.0f, 0.0f,
    0.0f, -1.0f, 0.0f,  0.0f, -1.0f, 0.0f,  0.0f, -1.0f, 0.0f,  0.0f, -1.0f, 0.0f,
    
    0.0f, 0.0f, +1.0f,  0.0f, 0.0f, +1.0f,  0.0f, 0.0f, +1.0f,  0.0f, 0.0f, +1.0f,
    0.0f, 0.0f, -1.0f,  0.0f, 0.0f, -1.0f,  0.0f, 0.0f, -1.0f,  0.0f, 0.0f, -1.0f
  };
  
  const unsigned int indices[] =
  {
    0, 1, 2, 0, 2, 3,
		4, 5, 6, 4, 6, 7,
		8, 9, 10, 8, 10, 11,
		12, 13, 14, 12, 14, 15,
		16, 17, 18, 16, 18, 19,
    20, 21, 22, 20, 22, 23
  };
  
  const unsigned int numIndices = sizeof(indices) / sizeof(unsigned int);
  
  setAttrib( POSITION, vertices, 3, sizeof(vertices)/3);
  setAttrib( TEXCOORD, texCoords, 2, sizeof(texCoords)/2);
  setAttrib( NORMAL, normals, 3, sizeof(normals)/3);
  setIndices( GL_TRIANGLES, indices, numIndices);
  
  upload( aer::DeviceBuffer::STATIC_DRAW, SINGLE_BUFFER);
}


void Plane::init(const float width, const float height, const unsigned int resolution)
{
  AER_ASSERT( "(warning) not implemented yet." && 0 );
}

